package com.xci.platform.auth.service;

import cn.hutool.core.convert.Convert;
import com.xci.platform.annotation.OperateUser;
import com.xci.platform.auth.core.AuthConstant;
import com.xci.platform.auth.core.HistoryModel;
import com.xci.platform.auth.core.HistoryOperateType;
import com.xci.platform.auth.dao.ModuleDao;
import com.xci.platform.auth.entity.ModuleEntity;
import com.xci.platform.core.*;
import com.xci.platform.helper.CoreHelper;
import com.xci.platform.helper.ObjectHelper;
import com.xci.platform.helper.StringHelper;
import com.xci.platform.helper.TreeHelper;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 系统模块服务
 */
@SuppressWarnings("unchecked")
@Service
public class ModuleService extends AuthBaseService {

    /**
     * 模块数据访问对象
     */
    @Resource
    private ModuleDao moduleDao;

    /**
     * 树形结构操作服务对象
     */
    private TreeOperateService treeOperateService;

    /**
     * 获取树形结构操作服务对象
     */
    public TreeOperateService getTreeOperateService() {
        if (treeOperateService == null) {
            treeOperateService = new TreeOperateService(moduleDao);
        }
        return treeOperateService;
    }

    /**
     * 保存模块
     *
     * @param entity   模块对象
     * @param isCreate 是否新建
     */
    @Validated
    @OperateUser
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage save(ModuleEntity entity, Boolean isCreate) {
        //添加名称简拼
        entity.setSpell(StringHelper.getSpell(entity.getName()));
        //验证父节点
        if (StringHelper.isBlank(entity.getParentId())) {
            entity.setParentId(Constant.RootNodeId);
        }
        //模块名称验证
        if (moduleDao.existByCode(entity.getCode(), entity.getId()) > 0) {
            String msg = StringHelper.format("模块编码 {} 已经存在", entity.getName());
            return new BoolMessage(false, msg);
        }

        String msg;
        long startTime = CoreHelper.startWatch();
        if (isCreate) {
            entity.setPath(getTreeOperateService().buildNewPath(entity.getParentId()));//获取路径
            msg = StringHelper.format("新增模块 {}", entity.getName());
            moduleDao.insert(entity);
            //region 历史记录
            operateHistory(buildHistory()
                    .operateType(HistoryOperateType.Insert)
                    .keyValue(entity.getId())
                    .message(msg)
                    .after(entity)
            );
            //endregion
        } else {
            msg = StringHelper.format("修改模块 {}", entity.getName());
            ModuleEntity oldEntity = queryById(entity.getId());
            moduleDao.update(entity);
            //region 历史记录
            operateHistory(buildHistory()
                    .operateType(HistoryOperateType.Update)
                    .keyValue(entity.getId())
                    .message(msg)
                    .before(oldEntity)
                    .after(entity)
            );
            //endregion
        }
        // 操作日志
        operateLog(AuthConstant.SystemModule.Module, msg, CoreHelper.stopWatch(startTime));
        //更新节点叶子状态
        getTreeOperateService().updateLeafStatus();
        return BoolMessage.True;
    }

    /**
     * 删除模块
     *
     * @param id 模块主键
     */
    @Validated
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage delete(@NotEmpty(message = "请指定删除的模块主键") String id) {
        ModuleEntity currentEntity = moduleDao.queryById(id);
        if (currentEntity == null) return BoolMessage.fail("无效的模块主键");

        List<ModuleEntity> childs = (List<ModuleEntity>) TreeHelper.getChilds(moduleDao.query(), id, true);
        for (ModuleEntity entity : childs) {
            long startTime = CoreHelper.startWatch();
            moduleDao.delete(entity.getId());
            String msg = StringHelper.format("删除模块 {}", entity.getName());
            //region 历史记录
            operateHistory(buildHistory()
                    .operateType(HistoryOperateType.Delete)
                    .keyValue(entity.getId())
                    .message(msg)
                    .before(entity)
            );
            //endregion
            operateLog(AuthConstant.SystemModule.Module, msg, CoreHelper.stopWatch(startTime)); // 操作日志
        }
        //更新节点叶子状态
        getTreeOperateService().updateLeafStatus();
        return BoolMessage.True;
    }

    /**
     * 保存拖拽数据(父节点和路径)
     *
     * @param nodeIndexs 更新的节点数据
     */
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage updateNodeIndex(TreeNodeIndex[] nodeIndexs) {
        getTreeOperateService().updateParentAndPath(nodeIndexs);
        getTreeOperateService().rebuildPath(Constant.RootNodeId);
        return BoolMessage.True;
    }

    /**
     * 更新模块启用状态
     *
     * @param id     模块主键
     * @param status 是否启用(1:启用 0:禁用)
     */
    @Validated
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage updateStatus(@NotNull(message = "请指定更新状态的模块主键") String id,
                                    @NotNull(message = "请指定状态") Integer status) {
        long startTime = CoreHelper.startWatch();
        moduleDao.updateStatus(id, status);
        // 操作日志
        String msg = StringHelper.format("更新模块状态 主键: {} 更新后状态: {}", id, status);
        operateLog(AuthConstant.SystemModule.Module, msg, CoreHelper.stopWatch(startTime));
        return BoolMessage.True;
    }

    /**
     * 根据模块主键获取模块对象
     *
     * @param id 模块主键
     * @return 返回指定模块主键的模块对象
     */
    @Validated
    public ModuleEntity queryById(@NotNull(message = "请指定模块主键") String id) {
        return moduleDao.queryById(id);
    }

    /**
     * 根据用户主键获取模块列表(从缓存中读取)
     *
     * @param userId 用户主键
     */
    @Validated
    @Cacheable(cacheNames = "userPermissionCache", key = "'module:list:'+#userId")
    public List<ModuleEntity> queryCacheByUserId(@NotNull(message = "请指定用户主键") String userId) {
        return moduleDao.queryByUser(userId);
    }

    /**
     * 根据用户主键获取模块字典(从缓存中读取)
     *
     * @param userId 用户主键
     */
    @Validated
    @Cacheable(cacheNames = "userPermissionCache", key = "'module:map:'+#userId")
    public Map<String, ModuleEntity> queryCacheMapByUserId(@NotNull(message = "请指定用户主键") String userId) {
        List<ModuleEntity> list = moduleDao.queryByUser(userId);
        Map<String, ModuleEntity> map = new HashMap<>(list.size());
        for (ModuleEntity entity : list) {
            map.put(entity.getCode(), entity);
        }
        return map;
    }

    /**
     * 根据用户主键获取模块列表
     *
     * @param userId 用户主键
     */
    @Validated
    public List<ModuleEntity> queryByUserId(@NotNull(message = "请指定用户主键") String userId) {
        return moduleDao.queryByUser(userId);
    }

    /**
     * 根据角色主键获取模块列表
     * @param roleId 角色主键
     */
    @Validated
    public List<ModuleEntity> queryByRoleId(@NotNull(message = "请指定角色主键") String roleId){
        return moduleDao.queryByRole(roleId);
    }

    /**
     * 查询模块列表
     *
     * @return 返回符合查询条件的模块列表
     */
    public List<ModuleEntity> query(Map params) {
        if (params == null) params = new HashMap();
        Object nameObj = params.get("name");
        Object statusObj = params.get("status");
        Object isMenuObj = params.get("isMenu");
        List<ModuleEntity> list = moduleDao.query();
        if (ObjectHelper.isBlank(nameObj) && ObjectHelper.isBlank(statusObj)) {
            return list;
        }
        List<ModuleEntity> result = list;
        if (ObjectHelper.isNotBlank(nameObj)) {
            String name = nameObj.toString().toLowerCase();
            result = result.stream().filter(p -> p.getCode().toLowerCase().contains(name)
                    || p.getName().toLowerCase().contains(name) || (p.getSpell() != null && p.getSpell().toLowerCase().contains(name)))
                    .collect(Collectors.toList());
        }
        if (ObjectHelper.isNotBlank(statusObj)) {
            result = result.stream().filter(p -> p.getStatus().equals(Convert.toInt(statusObj))).collect(Collectors.toList());
        }
        if (ObjectHelper.isNotBlank(isMenuObj)) {
            result = result.stream().filter(p -> p.getIsMenu().equals(Convert.toInt(isMenuObj))).collect(Collectors.toList());
        }
        return (List<ModuleEntity>) TreeHelper.filter(list, result);
    }

    /**
     * 动态加载模块列表
     *
     * @param id 模块主键
     * @return 模块列表
     */
    public List<ModuleEntity> dynamicQuery(String id) {
        return moduleDao.dynamicQuery(id);
    }

    /**
     * 获取模块名称
     *
     * @param id 模块主键
     * @return 模块名称
     */
    public String queryNameById(String id) {
        return moduleDao.queryNameById(id);
    }

    /**
     * 转为节点列表
     *
     * @param modelList 模型列表
     */
    public List<TreeNode> convertToNodeList(List<ModuleEntity> modelList) {
        List<TreeNode> nodeList = new ArrayList<>();
        for (ModuleEntity item : modelList) {
            TreeNode node = new TreeNode();
            node.setId(item.getId());
            node.setPid(item.getParentId());
            node.setText(item.getName());
            node.setSpell(item.getSpell());
            node.setLeaf(item.getLeaf());
            node.setIconCls(item.getIconCls());
            node.setUrl(item.getUrl());
            String state = "open";
            if (item.getIsExpand() == 0 && modelList.stream().anyMatch(p -> p.getParentId().equals(item.getId()))) {
                state = "closed";
            }
            node.setState(state);
            nodeList.add(node);
        }
        return nodeList;
    }

    /**
     * 生成历史记录对象
     */
    private HistoryModel.HistoryModelBuilder buildHistory() {
        return HistoryModel.builder()
                .tableName("sys_module")
                .tableCaption("系统模块")
                .keyName("id");
    }
}
